/**
 * 中心数据管理器
 * @author tsdy
 * @description 类似于vue中的vuex
 * 数据中心是与组件分割开的，所有组件共用一个数据中心
 * 哪个组件需要响应式访问数据中心的数据，就需要提交监听器到数据中心
 */

import Dep from "./dep";
import { deepProxy, getGoalData } from "../utils/index";
import { DataCenterConfig } from "../typings/dataCenter";
import { FoundDependence } from "../typings/dep";
class DataCenter {
  public state: Record<string, any>;
  public dep: Dep;
  private mutations: Record<string, Function>;
  private Watcher: Record<number, Record<string, Record<string, Array<Function>>>> = {};
  constructor(config: DataCenterConfig) {
    const depName = 'data'
    let DepHash: Record<string, boolean> = {};
    let _state: Record<string, string | number> = config.state;
    this.mutations = config.mutations;
    Object.keys(_state).forEach((key) => {
      DepHash[key] = false;
    });
    this.dep = new Dep({
      DepHash,
    });
    var get = (path: string) => {
      this.dep.get(path);
    };
    var set = (path: string, value: any) => {
      let stoneDeps: Array<FoundDependence>;
      let num = Number(path.split(".").pop());
      if (!Number.isNaN(num)) {
        let arr_path: Array<string> = path.split(".");
        arr_path.pop();
        stoneDeps = this.dep.findDepValue(String(arr_path).replace(",", "."));
      } else if (path.includes(".__proto__")) {
        /**
         * 由于将proxy上的数组原型上的方法重写。
         * 每次调用数组上的函数时，都会零时创建一个变量，深拷贝proxy上的数组
         * 这个深拷贝的数组会调用，原本应该由proxy上的数组调用的方法。
         * 最后将这个临时的数组进行深代理deepProxy(arr, get, set, path + key + ".");
         * 覆盖原来proxy上的数组。
         * 此时我们需要将刚刚深代理的数组上的方法重写，obj[key].__proto__ = arrayMethod;（file: deepProxy.ts）
         * set方法中就出现了.__proto__这个键名
         */
        stoneDeps = this.dep.findDepValue(<string>path.replace(".__proto__", ""));
        stoneDeps.forEach((item) => {
          const { id, key, value } = item;
          /**
           * 被修改属性的响应
           */
          if (id != undefined && key && value) {
            if (this.Watcher[id][<string>path.replace(".__proto__", "")]) {
              this.Watcher[id]['data'][<string>path.replace(".__proto__", "")].forEach(
                (fun: Function) => {
                  fun(
                    getGoalData(_state, <string>path.replace(".__proto__", ""))
                  );
                }
              );
            }
          }
        });
      } else {
        stoneDeps = this.dep.findDepValue(path);
      }
      stoneDeps.forEach((item) => {
        const { id, key, value } = item;
        if (id != undefined && key && value) {
          if (this.Watcher[id]) {
            if (this.Watcher[id][value]) {
              this.Watcher[id][item.depName][value].forEach((watcher) => {
                watcher(getGoalData(_state, key));
              });
            }
          }
        }
      });
    };
    this.state = deepProxy(_state, get, set, "");
  }
  public commit(name: string, ...args: any) {
    this.mutations[name](this.state, args);
  }
  /**
   *
   * @param name
   * @param id
   * @param watcher
   * 其它组件通过引入这个方法，来添加监听器
   */
  public addWatcher(value: string, id: number, depName: string, watcher: Function) {
    if (!this.Watcher[id]) {
      this.Watcher[id] = {};
    }
    if (!this.Watcher[id][depName]) {
      this.Watcher[id][depName] = {};
    }
    if (!this.Watcher[id][depName][value]) {
      this.Watcher[id][depName][value] = [];
    }
    this.Watcher[id][depName][value].push(watcher);
  }
}

export { DataCenter };
